﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using Moq;
using NUnit.Framework;
using SessionHunter.Business;
using SessionHunter.Model;

namespace SessionHunter.UnitTests.Business
{
    [TestFixture]
    public class TestReferenceManager
    {
        /// <summary>
        /// A fake path for mocks
        /// </summary>
        public const string FakePath = @"C:\NoDir\SomeAssembly.dll";
        /// <summary>
        /// A fake assembly name for mocks
        /// </summary>
        public const string FakeFullName = @"SomeAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";

        [Test]
        public void AddDynamicReference_AddNewItem_ItemAdded()
        {
            var manager = new Mock<ReferenceManager>() { CallBase = true };
            manager.Setup(mock => mock.LoadAssembly(FakePath)).Returns(FakeFullName);

            manager.Object.AddDynamicReference(FakePath);
            Assert.AreEqual(1, manager.Object.CurrentReferences.Count, "Reference did not get added by call to AddDynamicReference");
            var item = manager.Object.CurrentReferences[0];
            Assert.IsNull(item.Error);
            Assert.AreEqual(FakeFullName, item.FullAssemblyName);
        }

        [Test]
        public void AddDynamicReference_AddItemTwice_ItemUnloaded()
        {
            var manager = new Mock<ReferenceManager>() { CallBase = true };
            manager.Setup(mock => mock.LoadAssembly(FakePath)).Returns(FakeFullName);
            manager.Setup(mock => mock.UnloadReference(FakeFullName)).Verifiable("Unload not called as expected");

            manager.Object.AddDynamicReference(FakePath);
            manager.Object.AddDynamicReference(FakePath); // Add second time

            Assert.AreEqual(1, manager.Object.CurrentReferences.Count, "Reference did not get added by call to AddDynamicReference");
            var item = manager.Object.CurrentReferences[0];
            Assert.IsNull(item.Error);
            Assert.AreEqual(FakeFullName, item.FullAssemblyName);

            manager.Verify();
        }

        [Test]
        public void RemoveMissingAssemblies_NothingToRemove_DoNothing()
        {
            // Arrange
            var manager = new ReferenceManager();
            ObservableCollection<string> missingAssemblies = null;

            // Act
            manager.RemoveMissingAssemblies(missingAssemblies);

            // Assert
            Assert.IsNull(missingAssemblies);
        }

        [Test]
        public void RemoveMissingAssemblies_RemoveSingleItem_ItemRemoved()
        {
            // Arrange
            var manager = new Mock<ReferenceManager>() { CallBase = true };
            manager.Setup(mock => mock.LoadAssembly(FakePath)).Returns(FakeFullName);
            manager.Object.AddDynamicReference(FakePath);

            var missingAssemblies = new ObservableCollection<string>();
            missingAssemblies.Add(FakeFullName);

            // Act
            manager.Object.RemoveMissingAssemblies(missingAssemblies);

            // Assert
            Assert.AreEqual(0, missingAssemblies.Count, "Missing assembly was not removed as expected");
        }

        [Test]
        public void GetReferencedAssemblies_Errorstate_ReturnEmptyList()
        {
            // Assemble
            var manager = new ReferenceManager();

            var externalReference = new ExternalReference()
            {
                FullAssemblyName = FakeFullName,
                Path = FakePath,
                Error = "I am not working"
            };

            // Act
            List<string> dependencies = manager.GetReferencedAssemblies(externalReference);

            // Assert
            Assert.NotNull(dependencies);
            Assert.AreEqual(dependencies.Count, 0);
        }

        [Test]
        public void GetReferencedAssemblies_ExistingAssembly_ReturnList()
        {
            // Assemble
            var manager = new Mock<ReferenceManager>() { CallBase = true };
            manager.Setup(mock => mock.LoadAssembly(FakePath)).Returns(FakeFullName);
            manager.Object.AddDynamicReference(FakePath);

            // We are going to fake this out, by asking for info on the session.exe, which the unit test references
            var sessionAssembly = AppDomain.CurrentDomain.GetAssemblies().FirstOrDefault(t => t.ManifestModule.Name.ToLower() == "sessionhunter.exe");

            var externalReference = new ExternalReference()
            {
                FullAssemblyName = sessionAssembly.FullName,
                Path = sessionAssembly.Location
            };

            // Act
            List<string> dependencies = manager.Object.GetReferencedAssemblies(externalReference);

            // Assert
            Assert.NotNull(dependencies);
            Assert.Greater(dependencies.Count, 0);
            Assert.IsTrue(dependencies.Contains("PresentationCore, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"), "Did not find the reference to WPF as expected");
        }
    }
}
